# 声明：本代码仅供学习和研究目的使用。使用者应遵守以下原则：  
# 1. 不得用于任何商业用途。  
# 2. 使用时应遵守目标平台的使用条款和robots.txt规则。  
# 3. 不得进行大规模爬取或对平台造成运营干扰。  
# 4. 应合理控制请求频率，避免给目标平台带来不必要的负担。   
# 5. 不得用于任何非法或不当的用途。
#   
# 详细许可条款请参阅项目根目录下的LICENSE文件。  
# 使用本代码即表示您同意遵守上述原则和LICENSE中的所有条款。  


# -*- coding: utf-8 -*-
# @Author  : relakkes@gmail.com
# @Time    : 2024/4/6 14:54
# @Desc    : mediacrawler db 管理
import asyncio
from typing import Dict
from urllib.parse import urlparse

import aiofiles
import aiomysql

import config
from async_db import AsyncMysqlDB
from tools import utils
from var import db_conn_pool_var, media_crawler_db_var


class DatabaseManager:
    _instance = None
    _lock = asyncio.Lock()
    
    def __init__(self):
        self.pool = None
        self.async_db = None
        self._connection_retries = 3
        self._retry_delay = 1
        
    @classmethod
    async def get_instance(cls):
        if not cls._instance:
            async with cls._lock:
                if not cls._instance:
                    cls._instance = DatabaseManager()
        return cls._instance
        
    async def init_pool(self):
        """初始化数据库连接池"""
        if self.pool and not self.pool.closed:
            return
            
        async with self._lock:
            try:
                self.pool = await aiomysql.create_pool(
                    host=config.RELATION_DB_HOST,
                    port=config.RELATION_DB_PORT,
                    user=config.RELATION_DB_USER,
                    password=config.RELATION_DB_PWD,
                    db=config.RELATION_DB_NAME,
                    autocommit=True,
                    minsize=5,
                    maxsize=20,
                    pool_recycle=1800,
                    echo=True
                )
                self.async_db = AsyncMysqlDB(self.pool)
                
                # 更新上下文变量
                db_conn_pool_var.set(self.pool)
                media_crawler_db_var.set(self.async_db)
                
                utils.logger.info("[DatabaseManager.init_pool] Database connection pool initialized successfully")
            except Exception as e:
                utils.logger.error(f"[DatabaseManager.init_pool] Failed to initialize pool: {str(e)}")
                raise
                
    async def ensure_connected(self):
        """确保数据库连接可用"""
        if not self.pool or self.pool.closed:
            await self.init_pool()
        try:
            async with self.pool.acquire() as conn:
                async with conn.cursor() as cursor:
                    await cursor.execute("SELECT 1")
            return True
        except Exception as e:
            utils.logger.error(f"[DatabaseManager.ensure_connected] Connection test failed: {str(e)}")
            self.pool = None
            await self.init_pool()
            return True
            
    async def close(self):
        """关闭连接池"""
        if self.pool and not self.pool.closed:
            self.pool.close()
            await self.pool.wait_closed()
            self.pool = None
            self.async_db = None
            
            # 清理上下文变量
            db_conn_pool_var.set(None)
            media_crawler_db_var.set(None)
            
            utils.logger.info("[DatabaseManager.close] Connection pool closed")
            
    def get_pool(self):
        """获取连接池实例"""
        return self.pool
        
    def get_async_db(self):
        """获取异步数据库实例"""
        return self.async_db


async def init_mediacrawler_db():
    """
    初始化数据库链接池对象，并将该对象塞给media_crawler_db_var上下文变量
    Returns:

    """
    db_manager = await DatabaseManager.get_instance()
    await db_manager.init_pool()


async def init_db():
    """
    初始化db连接池
    Returns:

    """
    utils.logger.info("[init_db] start init mediacrawler db connect object")
    await init_mediacrawler_db()
    utils.logger.info("[init_db] end init mediacrawler db connect object")


async def close_db():
    """
    关闭连接池
    Returns:

    """
    db_manager = await DatabaseManager.get_instance()
    await db_manager.close()


async def init_table_schema():
    """
    用来初始化数据库表结构，请在第一次需要创建表结构的时候使用，多次执行该函数会将已有的表以及数据全部删除
    Returns:

    """
    utils.logger.info("[init_table_schema] begin init mysql table schema ...")
    db_manager = await DatabaseManager.get_instance()
    await db_manager.init_pool()
    
    async with aiofiles.open("schema/tables.sql", mode="r", encoding="utf-8") as f:
        schema_sql = await f.read()
        await db_manager.get_async_db().execute(schema_sql)
        utils.logger.info("[init_table_schema] mediacrawler table schema init successful")
        await db_manager.close()


if __name__ == '__main__':
    asyncio.get_event_loop().run_until_complete(init_table_schema())
